package com.xiaoxx.service.impl;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wf.captcha.ArithmeticCaptcha;
import com.xiaoxx.common.base.JwtUser;
import com.xiaoxx.common.error.ErrorException;
import com.xiaoxx.common.result.RType;
import com.xiaoxx.config.GiteeAuthConfig;
import com.xiaoxx.config.GiteeAuthUserConfig;
import com.xiaoxx.enums.LoginEnum;
import com.xiaoxx.enums.ResdisKeyEnum;
import com.xiaoxx.mapper.SysUserMapper;
import com.xiaoxx.model.dto.sys.*;
import com.xiaoxx.model.entity.sys.SysRole;
import com.xiaoxx.model.entity.sys.SysRoleUser;
import com.xiaoxx.model.entity.sys.SysUser;
import com.xiaoxx.model.vo.sys.*;
import com.xiaoxx.service.SysRoleService;
import com.xiaoxx.service.SysRoleUserService;
import com.xiaoxx.service.SysUserService;
import com.xiaoxx.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * @author :HX
 * @date : 2023/7/19
 */
@Service
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Resource
    private HttpServletResponse response;

    @Resource
    private HttpServletRequest request;

    @Resource
    private SysRoleUserService roleUserService;

    @Resource
    private SysRoleService roleService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private GiteeAuthConfig giteeAuthConfig;

    @Autowired
    private GiteeAuthUserConfig giteeAuthUserConfig;

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public Boolean register(UserRegisterDto sysUserDto) {
        //校验密码是否相同
        if (!sysUserDto.getVerifyPassword().equals(sysUserDto.getPassword())) {
            throw new ErrorException(500, "两次密码不相同！！");
        }
        if (sysUserDto.getUsername().equals(LoginEnum.SUPER_ADMIN.getUserName()) || sysUserDto.getUsername().equals(LoginEnum.SUPER_ADMIN.getFullName())) {
            throw new ErrorException(RType.USER_ACCOUNT_IS_DUPLICATE);
        }
        // 1、判断账号是否存在
        SysUser user = getByUserNameOrPhone(sysUserDto.getUsername(), 1);
        if (user != null) {
            throw new ErrorException(RType.USER_ACCOUNT_IS_DUPLICATE);
        }
        SysUser userPhone = getByUserNameOrPhone(sysUserDto.getUsername(), 2);
        if (userPhone != null) {
            throw new ErrorException(RType.USER_ACCOUNT_IS_DUPLICATE);
        }
        SysUser sysUser = sysUserDto.convert(SysUser.class);
        sysUser.setFullName(sysUser.getUsername());
        sysUser.setPassword(MD5Util.encode(sysUser.getPassword()));
        sysUser.setRegTime(LocalDateTime.now());
        boolean save = save(sysUser);
        if (save) {
            SysRole sysRole = new SysRole();
            switch (sysUserDto.getRegisterType()) {
                case 0:
                    // 1、用户的注册
                    sysRole = roleService.getByCode("USER");
                    break;
                case 1:
                    // 2、手机号登录
                    sysRole = roleService.getByCode("Normal");
                    break;
                default:
                    break;
            }
            if (sysRole != null) {
                //新增用户角色关系
                SysRoleUser roleUser = new SysRoleUser();
                roleUser.setUserId(sysUser.getId());
                roleUser.setRoleId(sysRole.getId());
                roleUserService.save(roleUser);
            }
        }
        return save;
    }


    @Override
    public SysUserLoginVo login(UserLoginDto userLogin) {
        //校验验证码
        String code = redisTemplate.opsForValue().get(userLogin.getCodeKey());
        if (!StringUtils.hasLength(code)) {
            throw new ErrorException(RType.LOGIN_EXPIRED_CODE);
        }
        if (!code.equals(userLogin.getCode())) {
            throw new ErrorException(RType.LOGIN_ERROR_CODE);
        }
        //成功 删除code
        redisTemplate.delete(userLogin.getCodeKey());
        // 1、判断账号
        SysUser user = getOne(new LambdaQueryWrapper<SysUser>()
                .and(i -> i.eq(SysUser::getUsername, userLogin.getUserName().trim())
                        .or().eq(SysUser::getPhone, AesUtil.encrypt(userLogin.getUserName().trim())))
                .last("limit 1")
        );
        if (user == null) {
            throw new ErrorException(RType.LOGIN_IS_NO_ACCOUNT);
        }
        // 2、判断密码
        if (!user.getPassword().equals(MD5Util.encode(userLogin.getPassword().trim()))) {
            throw new ErrorException(RType.LOGIN_ERROR_USER_PASSWORD);
        }
        // 3、判断禁用
        if (!user.getDisable().equals(0)) {
            throw new ErrorException(RType.LOGIN_IS_NO_DISABLE);
        }
        SysRole sysRole = roleService.getRoleNameByUserId(user.getId());
        if (userLogin.getType() == 1) {
            if (!sysRole.getCode().equals(LoginEnum.USER.getUserName())) {
                throw new ErrorException(RType.LOGIN_NOT_AUTH);
            }
        } else {
            if (sysRole.getCode().equals(LoginEnum.USER.getUserName())) {
                throw new ErrorException(RType.LOGIN_NOT_AUTH);
            }
            //判断是否有菜单
            if (!user.getUsername().equals(LoginEnum.SUPER_ADMIN.getUserName()) || !user.getFullName().equals(LoginEnum.SUPER_ADMIN.getFullName())) {
                List<String> currentMenuIds = roleUserService.findCurrentMenuIds(user.getId());
                if (currentMenuIds.isEmpty()) {
                    throw new ErrorException(RType.LOGIN_NOT_AUTH);
                }
            }
        }
        //登录成功
        JwtUser jwtUser = new JwtUser();
        jwtUser.setUserId(user.getId());
        jwtUser.setFullName(user.getFullName());
        // 设置token有效期(分)
        jwtUser.setExpiration(60);
        String token = JwtUtil.createToken(jwtUser, response);
        redisTemplate.opsForValue().set(ResdisKeyEnum.LOGIN_TOKEN.getRedisPath().concat(user.getId()), token, 60, TimeUnit.MINUTES);
        //刷新最后登录时间
        SysUser updUser = new SysUser();
        updUser.setId(user.getId());
        updUser.setEndTime(LocalDateTime.now());
        updateById(updUser);
        SysUserLoginVo convert = BeanDtoVoUtil.convert(user, SysUserLoginVo.class);
        convert.setRoleName(roleService.getRoleNameByUserId(user.getId()).getName());
        convert.setEndTime(updUser.getEndTime());
        return convert;
    }

    @Override
    public IPage<SysUserListVo> listUser(SysUserQuery query) {
        SysUser sysUser = getById(JwtUtil.getJwtUser(request).getUserId());
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StringUtils.hasLength(query.getUsername()), SysUser::getUsername, query.getUsername());
        wrapper.orderByDesc(SysUser::getUpdateTime);
        if (!(LoginEnum.SUPER_ADMIN.getUserName().equals(sysUser.getUsername()) && LoginEnum.SUPER_ADMIN.getFullName().equals(sysUser.getFullName()))) {
            wrapper.and(i -> i.ne(SysUser::getUsername, LoginEnum.SUPER_ADMIN.getUserName())
                    .or().ne(SysUser::getFullName, LoginEnum.SUPER_ADMIN.getFullName()));
        }
        Page<SysUser> page = page(new Page<SysUser>(query.getCurrent(), query.getSize()), wrapper);
        IPage<SysUserListVo> vo = BeanDtoVoUtil.pageVo(page, SysUserListVo.class);
        for (SysUserListVo record : vo.getRecords()) {
            record.setPhone(StringUtils.hasLength(record.getPhone()) ? DesensitizationUtil.phone(AesUtil.decrypt(record.getPhone())) : null);
        }
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(SysUserDto dto) {
        SysUser user = null;
        // 1、判断账号是否存在
        user = getByUserNameOrPhone(dto.getUsername(), 1);
        if (user != null) {
            throw new ErrorException(RType.USER_ACCOUNT_IS_DUPLICATE);
        }
        //2、校验电话码是否存在
        user = getByUserNameOrPhone(dto.getPhone(), 2);
        if (user != null) {
            throw new ErrorException(RType.USER_PHONE_IS_DUPLICATE);
        }
        //3、新增
        SysUser sysUser = dto.convert(SysUser.class);
        sysUser.setPassword(MD5Util.encode(sysUser.getUsername()));
        sysUser.setPhone(AesUtil.encrypt(dto.getPhone()));
        sysUser.setRegTime(LocalDateTime.now());
        boolean save = save(sysUser);
        if (save) {
            //新增用户角色关系
            SysRoleUser roleUser = new SysRoleUser();
            roleUser.setUserId(sysUser.getId());
            roleUser.setRoleId(dto.getRoleId());
            roleUserService.save(roleUser);
        }
        return save;
    }

    @Override
    public Boolean upd(SysUserDto dto) {
        SysUser sysUser = getById(dto.getId());
        if (!StringUtils.hasLength(sysUser.getPhone()) || !sysUser.getPhone().equals(AesUtil.encrypt(dto.getPhone()))) {
            //判断手机号是否存在
            SysUser user = getByUserNameOrPhone(dto.getPhone(), 2);
            if (user != null) {
                throw new ErrorException(RType.USER_PHONE_IS_DUPLICATE);
            }
        }
        sysUser = dto.convert(SysUser.class);
        sysUser.setPhone(AesUtil.encrypt(dto.getPhone()));
        boolean upd = updateById(sysUser);
        if (upd) {
            //修改用户角色关系
            roleUserService.update(new LambdaUpdateWrapper<SysRoleUser>()
                    .set(SysRoleUser::getRoleId, dto.getRoleId())
                    .eq(SysRoleUser::getUserId, sysUser.getId()));
        }
        return upd;
    }

    @Override
    public Boolean updDisable(String id, Integer disable) {
        return update(new LambdaUpdateWrapper<SysUser>()
                .set(SysUser::getDisable, disable)
                .eq(SysUser::getId, id));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean del(String id) {
        boolean b = removeById(id);
        if (b) {
            roleUserService.remove(new LambdaQueryWrapper<SysRoleUser>()
                    .eq(SysRoleUser::getUserId, id));
        }
        return b;
    }

    @Override
    public SysUserVo findById(String id) {
        SysUser sysUser = this.getById(id);
        SysUserVo convert = sysUser.convert(SysUserVo.class);
        convert.setPhone(StringUtils.hasLength(convert.getPhone()) ? AesUtil.decrypt(sysUser.getPhone()) : null);
        SysRoleUser one = getSysRoleUser(id);
        if (one != null) {
            convert.setRoleId(one.getRoleId());
        }
        return convert;
    }

    @Override
    public List<SysUserTreeListVo> findUserTree() {
        List<SysRole> list = roleService.list(new LambdaQueryWrapper<SysRole>()
                .eq(SysRole::getDisable, 0));
        List<SysUserTreeListVo> vos = list.stream().map(i -> {
            SysUserTreeListVo vo = new SysUserTreeListVo();
            vo.setRoleId(i.getId());
            vo.setRoleName(i.getName());
            return vo;
        }).collect(Collectors.toList());
        List<SysUserVo> userRoleInfos = baseMapper.getUserRoleInfos();
        Map<String, List<SysUserVo>> collect = userRoleInfos.stream().collect(Collectors.groupingBy(SysUserVo::getRoleId));
        vos = vos.stream().map(i -> {
            i.setUserTreeInfoVos(BeanDtoVoUtil.listVo(collect.get(i.getRoleId()), SysUserTreeInfoVo.class));
            return i;
        }).filter(i -> !i.getUserTreeInfoVos().isEmpty()).collect(Collectors.toList());
        return vos;
    }

    @Override
    public String getIpInfo() {
        try {
            InetAddress localhost = InetAddress.getLocalHost();
            System.out.println(localhost.getHostAddress());
        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        }
        String ipAddr = IpUtil.getIpAddr(request);
        log.info("ip地址---------》{}", ipAddr);
        //本地化处理
        if (ipAddr.equals("127.0.0.1") || ipAddr.startsWith("192.168.")) {
            return "四川省成都市";
        } else {
            return IpUtil.getAddress(ipAddr);
        }
    }


    @Override
    public SysUserVo getUserInfo() {
        String userId = JwtUtil.getJwtUser(request).getUserId();
        return findById(userId);
    }

    @Override
    public Boolean updPassword(SysUserPasswordDto dto) {
        if (!dto.getNewPassword().equals(dto.getConfirmPassword())) {
            throw new ErrorException(10001, "新密码与确认密码不一致");
        }
        String id = dto.getId();
        if (!StringUtils.hasLength(id)) {
            id = JwtUtil.getJwtUser(request).getUserId();
        }
        SysUser sysUser = getById(id);
        if (sysUser == null) {
            throw new ErrorException(10001, "用户不存在");
        }
        //校验密码
        if (!sysUser.getPassword().equals(MD5Util.encode(dto.getOldPassword()))) {
            throw new ErrorException(10001, "密码错误!");
        }
        sysUser.setPassword(MD5Util.encode(dto.getNewPassword()));
        return updateById(sysUser);
    }

    @Override
    public Map<String, String> getCode() {
        // 算术类型 https://gitee.com/whvse/EasyCaptcha
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(111, 36);
        // 几位数运算，默认是两位
        captcha.setLen(3);
        // 获取运算的结果
        String result = "";
        try {
            result = new Double(Double.parseDouble(captcha.text())).intValue() + "";
        } catch (Exception e) {
            result = captcha.text();
        }
        //自定义redis Key  常量字符串+UUID
        String uuid = ResdisKeyEnum.LOGIN_CODE.getRedisPath() + IdUtil.simpleUUID();
        // 保存验证码运算结果到redis
        redisTemplate.opsForValue().set(uuid, result, 5, TimeUnit.MINUTES);
        // 验证码信息
        Map<String, String> imgResult = new HashMap<String, String>(2) {{
            put("img", captcha.toBase64());
            put("uuid", uuid);
        }};
        return imgResult;
    }

    @Override
    public String getGiteeUrl(String type) {
        String url = "https://gitee.com/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code";
        ;
        if (type.equals("admin")) {
            url = url.replace("{client_id}", giteeAuthConfig.getClientId()).replace("{redirect_uri}", giteeAuthConfig.getCallbackUrl());
        } else {
            url = url.replace("{client_id}", giteeAuthUserConfig.getClientId()).replace("{redirect_uri}", giteeAuthUserConfig.getCallbackUrl());
        }
        return url;
    }

    @Override
    public String callback(String code) {
        // Step2：通过Authorization Code获取Access Token
        String url = "https://gitee.com/oauth/token?grant_type=authorization_code" +
                "&client_id=" + giteeAuthConfig.getClientId() +
                "&client_secret=" + giteeAuthConfig.getClientSecret() +
                "&code=" + code +
                "&redirect_uri=" + giteeAuthConfig.getCallbackUrl();
        try {
            JSONObject accessToken = restTemplate.postForObject(url, null, JSONObject.class);
            String infoUrl = "https://gitee.com/api/v5/user?access_token=" + accessToken.get("access_token");
            JSONObject userInfo = restTemplate.getForObject(infoUrl, JSONObject.class);
            String giteeId = userInfo.get("id").toString();
            if (StringUtils.hasLength(giteeId)) {
                String giteeName = userInfo.get("name").toString();
                String giteeAvatarUrl = userInfo.get("avatar_url").toString();
                String giteeLogin = userInfo.get("login").toString();
                SysUser sysUser = getBYGiteeId(giteeId);
                if (sysUser == null) {
                    sysUser = new SysUser();
                    sysUser.setUsername(giteeLogin);
                    sysUser.setPassword(MD5Util.encode("123456"));
                    sysUser.setGiteeId(giteeId);
                    sysUser.setHeadPic(giteeAvatarUrl);
                    sysUser.setFullName(giteeName);
                    sysUser.setRegTime(LocalDateTime.now());
                    boolean save = save(sysUser);
                    if (save) {
                        SysRole sysRole = roleService.getByCode("Normal");
                        if (sysRole != null) {
                            //新增用户角色关系
                            SysRoleUser roleUser = new SysRoleUser();
                            roleUser.setUserId(sysUser.getId());
                            roleUser.setRoleId(sysRole.getId());
                            roleUserService.save(roleUser);
                        }
                    }
                }
                // 5、生成jwt
                JwtUser jwtUser = new JwtUser();
                jwtUser.setUserId(sysUser.getId());
                jwtUser.setFullName(sysUser.getFullName());
                // 设置token有效期(分)
                jwtUser.setExpiration(60);
                String token = JwtUtil.createToken(jwtUser, response);
                redisTemplate.opsForValue().set(ResdisKeyEnum.LOGIN_TOKEN.getRedisPath().concat(sysUser.getId()), token, 60, TimeUnit.MINUTES);
                return token;
            }
            return null;
        } catch (Exception e) {
            log.info("授权失败！！！");
            return null;
        }
    }

    @Override
    public SysUserLoginVo loginByToken(Integer type) {
        JwtUser jwtUser = JwtUtil.getJwtUser(request);
        SysUser user = getById(jwtUser.getUserId());
        if (user == null) {
            throw new ErrorException(RType.LOGIN_IS_NO_ACCOUNT);
        }
        // 3、判断禁用
        if (!user.getDisable().equals(0)) {
            throw new ErrorException(RType.LOGIN_IS_NO_DISABLE);
        }
        SysRole sysRole = roleService.getRoleNameByUserId(user.getId());
        if (type == 1) {
            if (!sysRole.getCode().equals(LoginEnum.USER.getUserName())) {
                throw new ErrorException(RType.LOGIN_NOT_AUTH);
            }
        } else {
            if (sysRole.getCode().equals(LoginEnum.USER.getUserName())) {
                throw new ErrorException(RType.LOGIN_NOT_AUTH);
            }
            //判断是否有菜单
            if (!user.getUsername().equals(LoginEnum.SUPER_ADMIN.getUserName()) || !user.getFullName().equals(LoginEnum.SUPER_ADMIN.getFullName())) {
                List<String> currentMenuIds = roleUserService.findCurrentMenuIds(user.getId());
                if (currentMenuIds.isEmpty()) {
                    throw new ErrorException(RType.LOGIN_NOT_AUTH);
                }
            }
        }
        // 6、刷新最后登录时间
        SysUser updUser = new SysUser();
        updUser.setId(user.getId());
        updUser.setEndTime(LocalDateTime.now());
        updateById(updUser);
        SysUserLoginVo convert = BeanDtoVoUtil.convert(user, SysUserLoginVo.class);
        convert.setRoleName(roleService.getRoleNameByUserId(user.getId()).getName());
        convert.setEndTime(updUser.getEndTime());
        return convert;
    }

    @Override
    public Boolean loginOut() {
        JwtUser jwtUser = JwtUtil.getJwtUser(request);
        return redisTemplate.delete(ResdisKeyEnum.LOGIN_TOKEN.getRedisPath().concat(jwtUser.getUserId()));

    }

    @Override
    public String userCallback(String code) {
        // Step2：通过Authorization Code获取Access Token
        String url = "https://gitee.com/oauth/token?grant_type=authorization_code" +
                "&client_id=" + giteeAuthUserConfig.getClientId() +
                "&client_secret=" + giteeAuthUserConfig.getClientSecret() +
                "&code=" + code +
                "&redirect_uri=" + giteeAuthUserConfig.getCallbackUrl();
        try {
            JSONObject accessToken = restTemplate.postForObject(url, null, JSONObject.class);
            String infoUrl = "https://gitee.com/api/v5/user?access_token=" + accessToken.get("access_token");
            JSONObject userInfo = restTemplate.getForObject(infoUrl, JSONObject.class);
            String giteeId = userInfo.get("id").toString();
            if (StringUtils.hasLength(giteeId)) {
                String giteeName = userInfo.get("name").toString();
                String giteeAvatarUrl = userInfo.get("avatar_url").toString();
                String giteeLogin = userInfo.get("login").toString();
                SysUser sysUser = getBYGiteeId(giteeId);
                if (sysUser == null) {
                    sysUser = new SysUser();
                    sysUser.setUsername(giteeLogin);
                    sysUser.setPassword(MD5Util.encode("123456"));
                    sysUser.setGiteeId(giteeId);
                    sysUser.setHeadPic(giteeAvatarUrl);
                    sysUser.setFullName(giteeName);
                    sysUser.setRegTime(LocalDateTime.now());
                    boolean save = save(sysUser);
                    if (save) {
                        SysRole sysRole = roleService.getByCode("USER");
                        if (sysRole != null) {
                            //新增用户角色关系
                            SysRoleUser roleUser = new SysRoleUser();
                            roleUser.setUserId(sysUser.getId());
                            roleUser.setRoleId(sysRole.getId());
                            roleUserService.save(roleUser);
                        }
                    }
                }
                // 5、生成jwt
                JwtUser jwtUser = new JwtUser();
                jwtUser.setUserId(sysUser.getId());
                jwtUser.setFullName(sysUser.getFullName());
                // 设置token有效期(分)
                jwtUser.setExpiration(60);
                String token = JwtUtil.createToken(jwtUser, response);
                redisTemplate.opsForValue().set(ResdisKeyEnum.LOGIN_TOKEN.getRedisPath().concat(sysUser.getId()), token, 60, TimeUnit.MINUTES);
                return token;
            }
            return null;
        } catch (Exception e) {
            log.info("授权失败！！！");
            return null;
        }
    }


    private SysUser getBYGiteeId(String giteeId) {
        return lambdaQuery()
                .eq(SysUser::getGiteeId, giteeId)
                .last("limit 1")
                .one();
    }


    private SysRoleUser getSysRoleUser(String id) {
        SysRoleUser one = roleUserService.getOne(new LambdaQueryWrapper<SysRoleUser>()
                .eq(SysRoleUser::getUserId, id)
                .last("limit 1"));
        return one;
    }


    /**
     * @param userNameOrPhone 用户名/电话号码
     * @param type            类型 1-用户名 2-电话好
     * @return
     */
    private SysUser getByUserNameOrPhone(String userNameOrPhone, int type) {
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        if (1 == type) {
            wrapper.eq(SysUser::getUsername, userNameOrPhone);
        } else {
            wrapper.eq(SysUser::getPhone, AesUtil.encrypt(userNameOrPhone));
        }
        wrapper.last("limit 1");
        return getOne(wrapper);
    }


}
